Contents page

Rules for Tools/Accessory Structure


The Accessory Structure

As with the ToolMaster structure, there is a standard Accessory structure that Bars&Pipes uses to keep track of your Accessory:


struct Accessory {
    struct Accessory *next;     /* Next in list. */
    long id;                    /* Unique ID. */
    struct Image *image;        /* Icon for window. */
    struct Image *onimage;      /* Icon when selected. */
    char name[100];             /* Name. */
    char filename[100];         /* Accessory file. */
    struct Window *window;      /* Control window. */
    unsigned short left, top;   /* Window position. */
    unsigned short width, height;/* Window size. */
    unsigned short x,y;         /* Icon position. */
    long (*remove)();           /* To remove Accessory. */
    long (*edit)();             /* Process IntuiMessage. */
    long (*open)();             /* Open window. */
    long (*close)();            /* Close window. */
    long (*size)();             /* Returns size for save. */
    long (*save)();             /* Save data to file. */
    long (*load)();             /* Load data from file. */
    long (*install)();          /* Install environment. */
    long (*clear)();            /* Clear environment. */
    long (*expandc)();
    long segment;               /* Segment list. */
    long altsegment;
    char selected;              /* Icon selected flag. */ };
When Bars&Pipes loads your Accessory, it calls a routine in the Accessory module's code named `inittoolmaster()' which initializes the Accessory structure and returns a pointer to it. Needless to say, this routine looks very similar to the `inittoolmaster()' routine provided by each Tool. It clears the Accessory structure, sets the name field, and initialises pointers to the supplied routines as well as initializing the icon for the Accessory window.

Here's sample code from the MIDI File reader/writer - MuFFy:

static struct Accessory MuFFy;

struct Accessory * inittoolmaster() {
    memset((char *)&MuFFy,0,sizeof(struct Accessory));
    MuFFy.image = &muffyoffimage;
    MuFFy.onimage = &muffyonimage;
    MuFFy.open = openwindow;
    MuFFy.close = closewindow;
    MuFFy.edit = editwindow;
    strcpy(MuFFy.name,"MuFFy");
    return(&MuFFy); }
	
Bars&Pipes handles Tool and Accessory control windows in very different ways. With Tools, it spins off a separate task and calls the Tool's edit code from within that task. The task handles the window interface and is removed only when the window is closed. With Accessories, the window interface is run from the main Bars&Pipes process, not from a separate task. This method has the advantage of keeping the Accessory control window synchronized with the rest of the Bars&Pipes user interface.

To do this, Bars&Pipes requires three routines, rather than one, to handle opening, using, and closing the Accessory's control window. In the case of MuFFy, these three routines are openwindow(), `editwindow()', and `closewindow()'.

When the user double clicks on MuFFy's icon in the Accessory Window, Bars&Pipes calls `openwindow()' to open MuFFy's control window. `openwindow()' opens the control window without a message port, then installs the message port from the main Bars&Pipes window. This instructs Intuition to send all events to that one message port, where Bars&Pipes can process them all at once.

struct Window * openwindow() {
    if (!MuFFy.window) {
        newwindow->Screen = functions->screen;
        newwindow->IDCMPFlags = 0;
        MuFFy.window = (struct Window *) OpenWindow(newwindow);
        if (MuFFy.window) {
            MuFFy.window->UserPort = functions->window->UserPort;
            ModifyIDCMP(MuFFy.window, functions->window->IDCMPFlags);
        }
    }
    return(MuFFy.window); }
Of course, if you are using the InovaTools routines, substitute `(*functions->FlashyOpenWindow)()' for `OpenWindow()'. In addition to opening the window with an exploding rectangle, this activates the saxophone pointer. (We've augmented some of the InovaTools routines!)

Notice that there is no need to make a duplicate of the NewWindow structure because, unlike Tool windows, duplicate Accessory windows can never be opened at the same time.

Once the window is open, Bars&Pipes keeps track of all incoming Intuition events.
Should an event arrive for MuFFy's window (for example, the user clicks on the Save gadget,) Bars&Pipes calls the `editwindow()' routine, passing the IntuiMessage as a parameter. The `editwindow()' routine processes the IntuiMessage, taking whatever actions are necessary. In our example, it responds to a click on the Save gadget by saving the current song to disk in the MIDI File format. Before it returns, the routine must always give Intuition back the message with the ReplyMsg command.

editwindow(message) struct IntuiMessage *message; {
    struct Window *window = MuFFy.window;
    struct Gadget *gadget;
    long class, code;
    class = message->Class;
    code = message->Code;
    gadget = (struct Gadget *) message->IAddress;
    ReplyMsg(message);
    /*       Process the Intuition command. */ }
Usually, Accessories do most of their work from within the editwindow routine, because this routine processes all of the input from Intuition.

Remember: Unlike Tool control windows, the Accessory does not run from a seperate Task, so if you need to do DOS i/o, you don't need to use the `doscall()' routine.

Finally, the last routine, `closewindow()', closes the window. This can be called from within the `editwindow()' routine, should the user click on the window's close gadget, or it can be called from Bars&Pipes, should it be necessary to do so because the user is closing the program, or going to interlace, etc.

Because this window shares a message port with other windows, it must follow the standard Intuition technique for closing a window safely. Also, in case the user moves the window, it should save the new window coordinates and size in the NewWindow structure, so the window will open next time in the new position.

void closewindow() {
    if (MuFFy.window) {
        newwindow.LeftEdge = MuFFy.window->LeftEdge;
        newwindow.TopEdge = MuFFy.window->TopEdge;
        newwindow.Width = MuFFy.window->Width;
        newwindow.Height = MuFFy.window->Height;
        MuFFy.window->UserPort = 0;
        ModifyIDCMP(MuFFy.window,0);
        WaitTOF();
        WaitTOF();
        WaitTOF();
        Forbid();
        while (message = (struct IntuiMessage *)
                GetMsg(functions->window->UserPort))
        ReplyMsg(message);
        CloseWindow(MuFFy.window);
        MuFFy.window = 0;
        Permit();
    } 
}
Once again, if you are using InovaTools, you may substitute `(*functions->FlashyCloseWindow)()' for the `CloseWindow()' call.